use std::error::Error;
use std::fs::File;

// 可恢复的错误 Result
#[cfg(test)]
mod panic_test {
    use std::fs::File;
    use std::io::{ErrorKind, Read};
    use std::path::Path;
    use std::{env, fs, io};

    #[test]
    fn match_err() {
        let buf = env::current_dir().expect("TODO: panic message");
        println!("work path {:?}", buf);
        let path = buf.join(Path::new("src/basex/err/hello.txt"));

        let f = File::open(&path);
        let f = match f {
            Ok(file) => file,
            Err(error) => match error.kind() {
                ErrorKind::NotFound => match File::create(path.as_path()) {
                    Ok(fc) => fc,
                    Err(e) => panic!("Problem creating the file: {:?}", e),
                },
                ErrorKind::NotADirectory => match File::create(path.as_path()) {
                    Ok(fc) => fc,
                    Err(e) => panic!("Problem creating the file: {:?}", e),
                },
                other_error => panic!("Problem opening the file: {:?}", other_error),
            },
            // Err(error) => {
            //     panic!("Problem opening the file: {:?}", error)
            // }
        };
    }

    #[test]
    fn cc() {
        let buf = env::current_dir().expect("TODO: panic message");
        println!("work path {:?}", buf);

        let exe_path = env::current_exe().expect("TODO: panic message");
        println!("exe path {:?}", exe_path)
    }

    #[test]
    fn method() {
        let buf = env::current_dir().expect("TODO: panic message");
        let filepath = buf.join("src/basex/err/hello.txt");
        println!("{:?}", filepath);
        // value or panic
        let f = File::open(filepath).unwrap();

        // value or panic msg
        let f = File::open("hello.txt").expect("EXPECT FAILED TO OPEN hello.txt");
    }

    // 错误传递
    fn read_username_from_file() -> Result<String, io::Error> {
        let buf = env::current_dir().expect("TODO: panic message");
        println!("work path {:?}", buf);
        let path = buf.join(Path::new("src/basex/err/hello.txt"));

        // 打开文件，f是`Result<文件句柄,io::Error>`
        let f = File::open(path);

        let mut f = match f {
            // 打开文件成功，将file句柄赋值给f
            Ok(file) => file,
            // 打开文件失败，将错误返回(向上传播)
            Err(e) => return Err(e),
        };
        // 创建动态字符串s
        let mut s = String::new();
        // 从f文件句柄读取数据并写入s中
        match f.read_to_string(&mut s) {
            // 读取成功，返回Ok封装的字符串
            Ok(_) => Ok(s),
            // 将错误向上传播
            Err(e) => Err(e),
        }
    }

    // 使用 ？ 简化：自动进行类型提升（转换）--》Result,Option
    // 根本原因是在于标准库中定义的 From 特征，该特征有一个方法 from，用于把一个类型转成另外一个类型，? 可以自动调用该方法，然后进行隐式类型转换
    fn read_username_err() -> Result<String, Box<dyn std::error::Error>> {
        let buf = env::current_dir().expect("TODO: panic message");
        println!("work path {:?}", buf);
        let path = buf.join(Path::new("src/basex/err/hello.txt"));

        let mut f = File::open(path)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        Ok(s)
    }

    // 链式调用
    fn read_username_link() -> Result<String, io::Error> {
        let mut s = String::new();
        File::open("hello.txt")?.read_to_string(&mut s)?;

        Ok(s)
    }

    // 标准库方法
    fn read_username_fs() -> Result<String, io::Error> {
        // read_to_string是定义在std::io中的方法，因此需要在上面进行引用
        fs::read_to_string("hello.txt")
    }

    // let v = xxx()?; //? 操作符需要一个变量来承载正确的值
    // xxx()?.yyy()?;
    fn option_err(arr: &[i32]) -> Option<&i32> {
        // let v = arr.get(0)?;
        // Some(v)

        arr.get(0)
    }

    fn last_char_of_first_line(text: &str) -> Option<char> {
        text.lines().next()?.chars().last()
    }

    // 1.13 版本-> ？，，
    macro_rules! try_or_log {
        ($expr:expr) => {
            match $expr {
                Ok(val) => val,
                Err(e) => {
                    eprintln!("Error: {:?}", e);
                    return Err(e.into());
                }
            }
        };
    }

    fn might_fail(ok: bool) -> Result<&'static str, &'static str> {
        if ok {
            Ok("success")
        } else {
            Err("something went wrong")
        }
    }

    fn main() -> Result<(), Box<dyn std::error::Error>> {
        let res = try_or_log!(might_fail(true));
        println!("res = {}", res);

        let res2 = try_or_log!(might_fail(false));
        println!("res2 = {}", res2);

        Ok(())
    }
}

fn main() -> Result<(), Box<dyn Error>> {
    let f = File::open("hello.txt")?;
    Ok(())
}
